import logging

from django.core.exceptions import ObjectDoesNotExist
from django.db.models.signals import post_save
from django.shortcuts import render
from rest_framework import status
from django.utils import timezone
from django.db.models import Count
from rest_framework.decorators import (api_view, parser_classes,
                                       permission_classes)
from rest_framework.parsers import FormParser, MultiPartParser
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.response import Response
from utils.filters import (VulnerableinstanceFilter,
                           paginate_queryset)
from utils.permissions import custom_permission_required

from ..models import (Project, Vulnerability,
                     Vulnerableinstance)
from ..nessus import is_valid_csv
from ..serializers import (Instanceserializers,
                          Vulnerabilityserializers, PublishVulnerabilitySerializer)

logger = logging.getLogger(__name__)



@api_view(['POST'])
@permission_classes([IsAuthenticated,IsAdminUser])
@custom_permission_required(['Manage Projects'])
@parser_classes([MultiPartParser,FormParser])
def Nessus_CSV(request, pk):
    try:
        Project.objects.get(pk=pk)
    except ObjectDoesNotExist:
        logger.error("Project Not Found, Project: %s is incorrect", pk)
        return Response({"message": "Project not found"}, status=status.HTTP_404_NOT_FOUND)
    file = request.FILES['file']
    output = is_valid_csv(file,pk)

    if not output:
        return Response({'status': 'Failed',"message": "Invalid CSV, Unable to parse or missing required fields"})

    else:
        allvuln = Vulnerability.objects.filter(project=pk)
        serializer = Vulnerabilityserializers(allvuln, many=True)
        return Response(serializer.data)


@api_view(['POST'])
@permission_classes([IsAuthenticated,IsAdminUser])
@custom_permission_required(['Manage Projects'])
def create_vulnerability(request):
    projectid = request.data.get('project')

    try:
        Projectobject = Project.objects.get(pk=projectid)
    except ObjectDoesNotExist:
        logger.error("Project Not Found, Project: %s is incorrect", projectid)
        return Response({"message": "Project not found"}, status=status.HTTP_404_NOT_FOUND)

    vulnserializer = Vulnerabilityserializers(data=request.data,context = {"request": request})
    if vulnserializer.is_valid(raise_exception=True):
        instacesserilization = Instanceserializers(data=request.data.get('instance'), many=True)
        if instacesserilization.is_valid():
            vulnerability = vulnserializer.save()
            instacesserilization.save(vulnerabilityid=vulnerability, project=Projectobject)
            respdata = vulnserializer.data
            respdata.update({'instance': instacesserilization.data})
            return Response(respdata, status=201)
        else:
            logger.error("instances are incorrect")
            return Response(vulnserializer.errors, status=400)
    else:
        logger.error(vulnserializer.errors)
        return Response(vulnserializer.errors, status=400)



@api_view(['GET'])
@permission_classes([IsAuthenticated,IsAdminUser])
def projectfindingview(request, pk):
    try:
        vulnerability = Vulnerability.objects.filter(project=pk)
        serializer = Vulnerabilityserializers(vulnerability, many=True,context={'exclude_instances': True})
        return Response(serializer.data)
    except ObjectDoesNotExist:
        logger.error("Project Vulnerrability not found for id=%s", pk)
        return Response({"message": "Vulnerrability not found"}, status=status.HTTP_404_NOT_FOUND)




@api_view(['POST'])
@permission_classes([IsAuthenticated,IsAdminUser])
@custom_permission_required(['Manage Projects'])
def projectvulnedit(request,pk):
    vulnerability = Vulnerability.objects.get(pk=pk)
    serializer = Vulnerabilityserializers(instance=vulnerability,data=request.data,context={'request': request})
    if serializer.is_valid(raise_exception=True):
        serializer.save()
        respdata={'Status':"Success"}
        respdata.update(serializer.data)
        return Response(respdata)
    else:
        logger.error("Serializer errors: %s", str(serializer.errors))
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)





@api_view(['DELETE'])
@permission_classes([IsAuthenticated,IsAdminUser])
@custom_permission_required(['Manage Projects'])
def projectvulndelete(request):
    vuln = Vulnerability.objects.filter(id__in=request.data)
    vuln.delete()
    respdata={'Status':"Success"}
    return Response(respdata)

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def projectvulnview(request,pk):
    try:
        user_company_id = request.user.company.id
        vulnerability = Vulnerability.objects.get(pk=pk)

        if not request.user.is_staff and not request.user.is_superuser:
            if user_company_id != vulnerability.project.companyname.id:
                return Response({"message": "You do not have permission to view this vulnerability."}, status=status.HTTP_403_FORBIDDEN)

        serializer = Vulnerabilityserializers(vulnerability, many=False, context={'request': request})
        instances = Vulnerableinstance.objects.filter(vulnerabilityid=pk)
        instancesserializer = Instanceserializers(instances,many=True)
        serialized_data = serializer.data
        serialized_data['instance'] = instancesserializer.data
        return Response(serialized_data)
        #return Response(serializer.data)
    except ObjectDoesNotExist:
        logger.error("Vulnerrability not found for id=%s", pk)
        return Response({"message": "Vulnerrability not found"}, status=status.HTTP_404_NOT_FOUND)




@api_view(['GET'])
@permission_classes([IsAuthenticated,IsAdminUser])
def projectvulninstances_filter(request,pk):
    sort_order = request.GET.get('order_by', 'desc')
    sort_field = request.GET.get('sort', 'id') or 'id'
    instancedetails = Vulnerableinstance.objects.filter(vulnerabilityid=pk)

    instance_filter = VulnerableinstanceFilter(request.GET, queryset=instancedetails)
    filtered_queryset = instance_filter.qs
    if sort_order == 'asc':
        filtered_queryset = filtered_queryset.order_by(sort_field)
    else:
        filtered_queryset = filtered_queryset.order_by('-'+sort_field)
    #filtered_queryset = instance_filter.qs
    paginator, paginated_queryset = paginate_queryset(filtered_queryset, request)
    serializer = Instanceserializers(paginated_queryset, many=True)

    return paginator.get_paginated_response(serializer.data)




@api_view(['GET'])
@permission_classes([IsAuthenticated,IsAdminUser])
def projectvulninstances(request,pk):
    instances = Vulnerableinstance.objects.filter(vulnerabilityid=pk)
    if not instances:
        logger.error("Vulnerrability Instance not found for id=%s", pk)
        return Response({"message": "Instancce not found"}, status=status.HTTP_404_NOT_FOUND)
    else:
        serializer = Instanceserializers(instances,many=True)
        return Response(serializer.data)



@api_view(['POST'])
@permission_classes([IsAuthenticated,IsAdminUser])
@custom_permission_required(['Manage Projects'])
def projectaddinstances(request,pk):
    try:
        vulnerability = Vulnerability.objects.get(pk=pk)
        project = vulnerability.project
        #request.data['vulnerabilityid'] = pk
        serializer = Instanceserializers(data=request.data,many=True)
        if serializer.is_valid(raise_exception=True):
            serializer.save(vulnerabilityid=vulnerability,project=project)
            return Response(serializer.data)
        else:
            logger.error("Serializer errors: %s", str(serializer.errors))
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    except ObjectDoesNotExist:
        logger.error("Vulnerrability not found for id=%s", pk)
        return Response({"message": "Vulnerrability not found"}, status=status.HTTP_404_NOT_FOUND)




@api_view(['POST'])
@permission_classes([IsAuthenticated,IsAdminUser])
@custom_permission_required(['Manage Projects'])
def projecteditinstances(request,pk):
    try:
        instance = Vulnerableinstance.objects.get(pk=pk)

        serializer = Instanceserializers(instance=instance,data=request.data,partial=True)
        if serializer.is_valid(raise_exception=True):
            serializer.save()
            return Response(serializer.data)
        else:
            logger.error("Serializer errors: %s", str(serializer.errors))
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    except ObjectDoesNotExist:
        logger.error("Instance not found for id=%s", pk)
        return Response({"message": "Instance not found"}, status=status.HTTP_404_NOT_FOUND)





@api_view(['DELETE'])
@permission_classes([IsAuthenticated,IsAdminUser])
@custom_permission_required(['Manage Projects'])
def projectdeleteinstances(request):
    vluninstace = Vulnerableinstance.objects.filter(id__in=request.data)
    if not vluninstace:
        logger.error("Instance not found")
        return Response({"message": "Instance not found"}, status=status.HTTP_404_NOT_FOUND)
    else:
        vluninstace.delete()
        respdata={'Status':"Success"}
        return Response(respdata)


@api_view(['POST'])
@permission_classes([IsAuthenticated,IsAdminUser])
@custom_permission_required(['Manage Projects'])
def projectinstancesstatus(request):
    vluninstace = Vulnerableinstance.objects.filter(id__in=request.data)
    if not vluninstace:
        logger.error("Instance not found")
        return Response({"message": "Instance not found"}, status=status.HTTP_404_NOT_FOUND)
    else:
        instancestatus = request.query_params.get('status')
        if instancestatus is None:
            logger.error("Status not defined")
            return Response({"message": "Missing 'status' parameter"}, status=status.HTTP_400_BAD_REQUEST)

        choices = dict(Vulnerableinstance.status.field.choices)
        if instancestatus not in choices:
            logger.error("Instance Status is not valid from %s", choices)
            return Response({"message": "Invalid status choice"}, status=status.HTTP_400_BAD_REQUEST)
        vluninstace.update(status=instancestatus)
        respdata = {'status': 'Success'}
        vulnerability_ids = vluninstace.values_list('vulnerabilityid', flat=True).distinct()
        for vulnerability_id in vulnerability_ids:
            vulnobject = Vulnerability.objects.get(id=vulnerability_id)
            post_save.send(sender=Vulnerability, instance=vulnobject, created=False)

        return Response(respdata)



@api_view(['GET'])
@permission_classes([IsAuthenticated,IsAdminUser])
@custom_permission_required(['Manage Projects'])
def projectvulnerabilitystatus(request,pk):
    try:
        vuln = Vulnerability.objects.get(pk=pk)
    except ObjectDoesNotExist:
        logger.error("Vulnerability not found for id=%s", pk)
        return Response({"Vulnerability": "Retest not found"}, status=status.HTTP_404_NOT_FOUND)

    instancestatus = request.query_params.get('status')
    if instancestatus is None:
        logger.error("Status not defined")
        return Response({"message": "Missing 'status' parameter"}, status=status.HTTP_400_BAD_REQUEST)

    choices = dict(Vulnerability.status.field.choices)
    if instancestatus not in choices:
        logger.error("Instance Status is not valid from %s", choices)
        return Response({"message": "Invalid status choice"}, status=status.HTTP_400_BAD_REQUEST)

    vluninstace = Vulnerableinstance.objects.filter(vulnerabilityid=pk)
    vluninstace.update(status=instancestatus)
    vuln.status = instancestatus
    vuln.save()
    respdata = {'status': 'Success'}
    return Response(respdata)

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def vulnerability_view(request, pk):
    is_staff = request.user.is_staff
    vulnerability = Vulnerability.objects.select_related('project__companyname').filter(pk=pk).first()
    if not vulnerability:
        return Response({"message": "Vulnerability not found."}, status=status.HTTP_404_NOT_FOUND)

    if not is_staff and vulnerability.project.companyname != request.user.company:
        return Response({"message": "You do not have permission to view this vulnerability."}, status=status.HTTP_403_FORBIDDEN)

    instances = Vulnerableinstance.objects.filter(vulnerabilityid=pk).select_related('vulnerabilityid')
    return render(request, 'vulnerabilities.html', {'vuln': [vulnerability], 'instances': instances})



@api_view(['POST'])
@permission_classes([IsAuthenticated, IsAdminUser])
@custom_permission_required(['Manage Projects'])
def publish_vulnerabilities(request):
    serializer = PublishVulnerabilitySerializer(data=request.data)
    if not serializer.is_valid():
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    ids = serializer.validated_data['ids']
    publish = serializer.validated_data['publish']

    # If not publishing, simply update without additional checks
    if not publish:
        affected_rows = Vulnerability.objects.filter(id__in=ids).update(
            published=False,
            published_date=None
        )

        return Response({
            "message": f"{affected_rows} vulnerabilities unpublished successfully.",
            "published": False
        }, status=status.HTTP_200_OK)

    # For publishing, check if vulnerabilities have instances

      # Get vulnerabilities with instance counts in a single query
    vulnerabilities = Vulnerability.objects.filter(id__in=ids).annotate(
        instance_count=Count('instances')
    )

    # Identify vulnerabilities without instances
    vulnerabilities_without_instances = list(
        vulnerabilities.filter(instance_count=0).values_list('id', 'vulnerabilityname')
    )

    # If any vulnerabilities lack instances, return error
    if vulnerabilities_without_instances:
        return Response({
            "error": "Cannot publish vulnerabilities without instances",
            "affected_vulnerabilities": [
                {"id": vuln_id, "name": vuln_name}
                for vuln_id, vuln_name in vulnerabilities_without_instances
            ]
        }, status=status.HTTP_400_BAD_REQUEST)

    # All vulnerabilities have instances, proceed with publishing
    affected_rows = vulnerabilities.update(
        published=True,
        published_date=timezone.now()
    )

    return Response({
        "message": f"{affected_rows} vulnerabilities published successfully.",
        "published": True
    }, status=status.HTTP_200_OK)



@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_project_vulnerability(request, pk):
    user_company_id = request.user.company.id
    project_company = Project.objects.filter(pk=pk).values_list('companyname', flat=True).first()
    if not project_company:
        return Response({"message": "Project not found."}, status=status.HTTP_404_NOT_FOUND)
    if not request.user.is_staff and not request.user.is_superuser:
        if user_company_id != project_company:
            return Response({"message": "You do not have permission to view this project."}, status=status.HTTP_403_FORBIDDEN)

    # Filter vulnerabilities by project and published status
    vulnerabilities = Vulnerability.objects.filter(project=pk, published=True)

    # Count vulnerabilities by severity
    severity_counts = {
        'Critical': vulnerabilities.filter(vulnerabilityseverity='Critical').count(),
        'High': vulnerabilities.filter(vulnerabilityseverity='High').count(),
        'Medium': vulnerabilities.filter(vulnerabilityseverity='Medium').count(),
        'Low': vulnerabilities.filter(vulnerabilityseverity='Low').count(),
        'None': vulnerabilities.filter(vulnerabilityseverity='None').count(),
    }

    # Select only the required fields
    vulnerabilities_data = vulnerabilities.values(
        'id', 'vulnerabilityname', 'vulnerabilityseverity',
        'cvssscore', 'status', 'published_date'
    )

    # Create response with vulnerabilities and severity counts
    response_data = {
        'vulnerabilities': vulnerabilities_data,
        'severity_counts': severity_counts
    }

    return Response(response_data)

